#include "ucdinputfile.h"

#include "cppunit/extensions/HelperMacros.h"
#include "cppunit/extensions/TestFactoryRegistry.h"
#include "cppunit/ui/text/TestRunner.h"

#include "visFlowClustering.h"

using namespace visualization;
string filename;

class VisFlowClusteringTest : public CppUnit::TestFixture
{
	CPPUNIT_TEST_SUITE(VisFlowClusteringTest);
	    CPPUNIT_TEST(testAccess);
		CPPUNIT_TEST(testSim);
		CPPUNIT_TEST(testInit);
		CPPUNIT_TEST(testMatrixMultiply);
		CPPUNIT_TEST(testSort);
		CPPUNIT_TEST(testGetEigenVec);
	CPPUNIT_TEST_SUITE_END();
	
public :
	VisFlowClusteringTest()
	{
	}
	~VisFlowClusteringTest(){}

	void setup()
	{
	}
	void tearDown(){}

	void testAccess();
	void testSim();
	void testInit();
	void testMatrixMultiply();
	void testSort();
	void testGetEigenVec();
private:
	visFlowClustering visFC;
	Model t;
};

CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(VisFlowClusteringTest, "flowclusteringtest");

void VisFlowClusteringTest::testAccess()
{
	int dim = 3;
	complex_ A[9];
	/* A = 
	 * /            \
	 * | 2   3   10 |
	 * | 7   9   22 |
	 * | 4   6   23 |
	 * \            /
	 */
	A[0].r = 2; A[1].r = 7; A[2].r = 4;
	A[3].r = 3; A[4].r = 9; A[5].r = 6;
    A[6].r = 10; A[7].r = 22; A[8].r = 23;

	for(int i = 0; i < dim * dim; i++)
	{
		A[i].i = 0;
	}

	double real, imag;
	visFC.getSquareMatrix(A, dim, 0, 0, real, imag);
	CPPUNIT_ASSERT(real = A[0].r && imag == 0);

	visFC.getSquareMatrix(A, dim, 0, 1, real, imag);
	CPPUNIT_ASSERT(real = A[3].r && imag == 0);

	visFC.getSquareMatrix(A, dim, 1, 0, real, imag);
	CPPUNIT_ASSERT(real = A[1].r && imag == 0);

	visFC.getSquareMatrix(A, dim, 1, 1, real, imag);
	CPPUNIT_ASSERT(real = A[4].r && imag == 0);

	visFC.getSquareMatrix(A, dim, 2, 2, real, imag);
	CPPUNIT_ASSERT(real = A[8].r && imag == 0);
}
void VisFlowClusteringTest::testSim()
{
	UCDInputFile file(filename);

	if(file.FGetModel(t) != 0)
	{
		exit(-1);
	}
	t.buildKDTree();
	visFC.setDataField(&t);
	double sim = visFC.sim(10, 10);
	double simm = visFC.sim(1, 230);
	double sim_ = visFC.sim(230, 1);

	CPPUNIT_ASSERT(sim == 1);
	CPPUNIT_ASSERT(simm > 0);
	CPPUNIT_ASSERT(simm < 1);
	CPPUNIT_ASSERT_DOUBLES_EQUAL(sim_, simm, 1e-10);
}
void VisFlowClusteringTest::testInit()
{
	UCDInputFile file(filename);
	if(file.FGetModel(t) != 0)
	{
		exit(-1);
	}
	t.buildKDTree();
	visFC.setDataField(&t);
	visFC.initMatrix();

	int len = visFC.getVecLen();
	for(int row = 0; row < len; row++)
	{
		for(int col = 0; col < len; col++)
		{
			//CPPUNIT_ASSERT_DOUBLES_EQUAL(visFC.simMatx[row * len + col].r, visFC.simMatx[col * len + row].r, 1e-10);
			CPPUNIT_ASSERT(visFC.getMatrixReal("sim", row, col) == visFC.getMatrixReal("sim", col, row));
		}
    }
	for(int i = 0; i < len; i++)
	{
		double rowSum = 0;
		for(int j = 0; j < len; j++)
		{
			rowSum += visFC.getMatrixReal("sim", i, j);
		}
		CPPUNIT_ASSERT_DOUBLES_EQUAL(rowSum, visFC.getMatrixReal("D", i, i), 1e-3);
	}
	for(int i = 0; i < len; i++)
	{
		CPPUNIT_ASSERT_DOUBLES_EQUAL(visFC.getMatrixReal("D", i, i), pow(1/visFC.getMatrixReal("D_", i, i), 2), 1e-3);
	}
}
void VisFlowClusteringTest::testMatrixMultiply()
{
	int dim = 2;
	complex_ A[4], B[4], result[4];
/*	// A = 
	// /         \
	// |  2    4 |
	// |  4    10|
	// \         /
	//
	// B = 
	// /         \
	// |  5    9 |
	// |  9    12|
	// \         /
	//
	double expected[4] = 
	{
		46, 66,
	    110, 156
	};

	A[0].r = 2;	A[1].r = 4; A[2].r = 4; A[3].r = 10; 
	A[0].i = A[1].i = A[2].i = A[3].i = 0;

	B[0].r = 5; B[1].r = 9; B[2].r = 9; B[3].r = 12;
	B[0].i = B[1].i = B[2].i = B[3].i = 0;
*/
	/** A = 
	 * /         \
	 * |  6    1 |
	 * |  1    3|
	 * \         /
	 *
	 * B = 
	 * /         \
	 * |  5    4 |
	 * |  4    10|
	 * \         /
	*/
	double expected[4] = 
	{
		34, 34,
	    17, 34
	};

	A[0].r = 6;	A[1].r = 1; A[2].r = 1; A[3].r = 3;
	A[0].i = A[1].i = A[2].i = A[3].i = 0;

	B[0].r = 5; B[1].r = 4; B[2].r = 4; B[3].r = 10;
	B[0].i = B[1].i = B[2].i = B[3].i = 0;

	visFC.ssMatrixMultiply(A, B, result, dim);

	CPPUNIT_ASSERT(result[0].r == expected[0] && result[0].i == 0);
	CPPUNIT_ASSERT(result[1].r == expected[2] && result[1].i == 0);
	CPPUNIT_ASSERT(result[2].r == expected[1] && result[2].i == 0);
	CPPUNIT_ASSERT(result[3].r == expected[3] && result[3].i == 0);
}
void VisFlowClusteringTest::testSort()
{
//	int num = 100;
	const int n = 10;
	
	complex_ array[n];
	for(int i = 0; i < n; i++)
	{
		array[i].r = rand();//num - i;//rand();
		array[i].i = 0;
	}

	double min = array[0].r;
	double max = min;
	for(int i = 1; i < n; i++)
	{
		if(min > array[i].r)
		{
			min = array[i].r;
		}
		if(max < array[i].r)
		{
			max = array[i].r;
		}
	}

	complex_ **pArray = new complex_*[n];
	for(int i = 0; i < n; i++)
	{
		pArray[i] = array + i;
	}
	visFC.sort(pArray, 0, n - 1);
	CPPUNIT_ASSERT(min == array[pArray[0] - array].r);
	CPPUNIT_ASSERT(max == array[pArray[n - 1] - array].r);
	delete []pArray;
}
void VisFlowClusteringTest::testGetEigenVec()
{
	/** The matrix:
	 * /           \
	 * |  20    23 |
	 * |  3      8 |
	 * \           /
	*/
	const int dimension = 2;
	complex_ matrix[dimension * dimension];
	for(int i = 0; i < dimension; i++)
	{
		matrix[i].i = 0;
	}
	matrix[0].r = 20;
	matrix[1].r =  3;
	matrix[2].r = 23;
	matrix[3].r =  8;

	complex_ eigVal[dimension], eigVec[dimension * dimension];
	visFC.calculator(matrix, dimension, eigVal, eigVec);

	CPPUNIT_ASSERT_DOUBLES_EQUAL(eigVal[0].r + eigVal[1].r, matrix[0].r + matrix[3].r, 1e-10);
	
	// Real symmetric matrix has real eigenvalues and eigenvectors.
	const int dimsn = 200;
	complex_ matr[dimsn * dimsn];
	complex_ eigVl[dimsn], eigVc[dimsn * dimsn];

	for(int i = 0; i < dimsn; i++)
	{
		for(int j = 0; j < dimsn; j++)
		{
			if(i == j)
			{
				visFC.setSquareMatrix(matr, dimsn, i, j, i + j + 1, 0);
			}
			else
			{
				visFC.setSquareMatrix(matr, dimsn, i, j, 0, 0);
			}
		}
	}
	visFC.calculator(matr, dimsn, eigVl, eigVc);

	for(int i = 0; i < dimsn; i++)
	{
		CPPUNIT_ASSERT(eigVl[i].i == 0);
		for(int j = 0; j < dimsn; j++)
		{
			CPPUNIT_ASSERT(eigVc[i * dimsn + j].i == 0);
		}
	}
}

int main(int argc, char *argv[])
{
	cout << "Input the data file: " << endl;
	cin >> filename;

	CppUnit::TextUi::TestRunner runner;
	CppUnit::TestFactoryRegistry &registry = CppUnit::TestFactoryRegistry::getRegistry("flowclusteringtest");
	runner.addTest(registry.makeTest());
	runner.run();
	return 0;
}

